package com.dream.libxrec.encode.audio;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class ADPCM {

    // Intel ADPCM step variation table
    private static final byte[] indexTable = {
        -1, -1, -1, -1, 2, 4, 6, 8,
        -1, -1, -1, -1, 2, 4, 6, 8,
    };

    private static final int[] stepsizeTable = {
        7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
        19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
        50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
        130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
        337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
        876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
        2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
        5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
        15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
    };

    public static class State {
        public short valprev;
        public int index;
    }

    public static byte[] encode(short[] indata, State state) {
        ByteBuffer outBuffer = ByteBuffer.allocate((indata.length + 1) / 2);
        outBuffer.order(ByteOrder.BIG_ENDIAN);

        short valpred = state.valprev;
        int index = state.index;
        int step = stepsizeTable[index];
        boolean bufferstep = true;
        byte outputbuffer = 0;

        for (short val : indata) {
            // ... [其余编码逻辑与C代码类似] ...
            // 注意：Java中所有数值都是有符号的，所以要注意处理负数。
            // 同时，移位操作对负数可能会产生不同的结果，需要特别注意。

            int diff = val - valpred;
            int sign = (diff < 0) ? 8 : 0;
            if (sign != 0) diff = -diff;

            int delta = 0;
            int vpdiff = step >> 3;

            if (diff >= step) {
                delta = 4;
                diff -= step;
                vpdiff += step;
            }
            step >>= 1;
            if (diff >= step) {
                delta |= 2;
                diff -= step;
                vpdiff += step;
            }
            step >>= 1;
            if (diff >= step) {
                delta |= 1;
                vpdiff += step;
            }

            if (sign != 0)
                valpred -= vpdiff;
            else
                valpred += vpdiff;

            if (valpred > Short.MAX_VALUE)
                valpred = Short.MAX_VALUE;
            else if (valpred < Short.MIN_VALUE)
                valpred = Short.MIN_VALUE;

            delta |= sign;

            index += indexTable[delta];
            if (index < 0) index = 0;
            if (index > 88) index = 88;
            step = stepsizeTable[index];

            if (bufferstep) {
                outputbuffer = (byte)((delta << 4) & 0xf0);
            } else {
                outBuffer.put((byte)(outputbuffer | (delta & 0x0f)));
            }
            bufferstep = !bufferstep;
        }

        if (!bufferstep)
            outBuffer.put(outputbuffer);

        state.valprev = valpred;
        state.index = index;

        return outBuffer.array();
    }

    public static short[] decode(byte[] indata,int size, State state) {
        ByteBuffer inBuffer = ByteBuffer.wrap(indata);
        inBuffer.order(ByteOrder.BIG_ENDIAN);

        short[] outdata = new short[indata.length * 2];
        short valpred = state.valprev;
        int index = state.index;
        int step = stepsizeTable[index];
        boolean bufferstep = false;

        for (int i = 0; i < size; i++) {
            byte inputbuffer = inBuffer.get();
            for (int j = 0; j < 2; j++) {
                int delta = (j == 0) ? ((inputbuffer >> 4) & 0xf) : (inputbuffer & 0xf);
                index += indexTable[delta];
                if (index < 0) index = 0;
                if (index > 88) index = 88;

                int sign = delta & 8;
                delta &= 7;

                int vpdiff = step >> 3;
                if ((delta & 4) != 0) vpdiff += step;
                if ((delta & 2) != 0) vpdiff += step >> 1;
                if ((delta & 1) != 0) vpdiff += step >> 2;

                if (sign != 0)
                    valpred -= vpdiff;
                else
                    valpred += vpdiff;

                if (valpred > Short.MAX_VALUE)
                    valpred = Short.MAX_VALUE;
                else if (valpred < Short.MIN_VALUE)
                    valpred = Short.MIN_VALUE;

                step = stepsizeTable[index];

                outdata[i * 2 + j] = valpred;
            }
        }

        state.valprev = valpred;
        state.index = index;

        return outdata;
    }
}